//https://dart.cn/language/#imports
//Import core libraries.
import 'dart:io';
import 'dart:math';
//Import libraries from external packages

//https://dart.cn/language/#enums
enum PlanetType { terrestrial, gas, ice }

enum Planet {
  mercury(planetType: PlanetType.terrestrial, moons: 0, hasRings: false),
  venus(planetType: PlanetType.terrestrial, moons: 0, hasRings: false),
  uranus(planetType: PlanetType.ice, moons: 27, hasRings: true),
  neptune(planetType: PlanetType.ice, moons: 14, hasRings: true);

  const Planet({
    required this.planetType,
    required this.moons,
    required this.hasRings,
  });

  //All instance variables are final
  final PlanetType planetType;
  final int moons;
  final bool hasRings;
  //Enhanced enums support getters and other methods
  bool get isGiant =>
      planetType == PlanetType.gas || planetType == PlanetType.ice;
}

void main(List<String> arguments) {
  //https://dart.cn/language/#hello-world
  print('Hello, World!');
  my_variables();
  var result = fabonacci(20);

  //https://dart.cn/language/#comments
  //This is a normal, one-line comment.
  ///This is a documentation comment, used to document libraries,
  ///Classes, and their members. Tools like IDEs and dartdoc treat
  ///doc comments specially.

  /*Comments like these are also supported.*/

  var voyager = Spacecraft('Voyager I', DateTime(1977, 9, 5));
  voyager.describe();
  var voyager3 = Spacecraft.unlaunched('Voyager III');
  voyager3.describe();

  final yourPlanet = Planet.mercury;
  if (!yourPlanet.isGiant) {
    print('Your palent is not a "giant palent".');
  }
}

void my_variables() {
  //https://dart.cn/language/#variables
  var name = 'Voyager I';
  var year = 1977;
  var antennaDiameter = 3.7;
  var flybyObjects = ['Jupiter', 'Saturn', 'Uranus', 'Neptune'];
  var image = {
    'tage': ['saturn'],
    'url': '//path/to/saturn.jpg',
  };

  //https://dart.cn/language/#control-flow-statements
  if (year >= 2001) {
    print('21st century');
  } else if (year >= 1901) {
    print('20th century');
  }

  for (final object in flybyObjects) {
    print(object);
  }

  for (int month = 1; month <= 12; month++) {
    print(month);
  }

  while (year < 2016) {
    year += 1;
  }

  flybyObjects.where((name) => name.contains('turn')).forEach(print);
}

int fabonacci(int n) {
  //https://dart.cn/language/#functions
  if (n == 0 || n == 1) return n;
  return fabonacci(n - 1) + fabonacci(n - 2);
}

class Spacecraft {
  //https://dart.cn/language/#classes
  String name;
  DateTime? launchDate;
  int? get launchYear => launchDate?.year;
  Spacecraft(this.name, this.launchDate) {
    //Initialization code goes here.
  }
  //Named constructor that forwards to the default one.
  Spacecraft.unlaunched(String name) : this(name, null);
  //method
  void describe() {
    print('Spacecraft: $name');
    var launchDate = this.launchDate;
    if (launchDate != null) {
      int year = DateTime.now().difference(launchDate).inDays ~/ 365;
      print('Launched: $launchYear($year years ago)');
    } else {
      print('Unlaunched');
    }
  }
}

//https://dart.cn/language/#inheritance
class Orbiter extends Spacecraft {
  double altitude;
  Orbiter(super.name, DateTime super.launchDate, this.altitude);
}

//https://dart.cn/language/#mixins
mixin Piloted {
  int astronauts = 1;
  void describeCrew() {
    print('Number of astronauts: $astronauts');
  }

  void testError() {
    //https://dart.cn/language/#exceptions
    if (astronauts == 0) {
      throw StateError('No astronauts.');
    }
  }
}

class PilotedCraft extends Spacecraft with Piloted {
  PilotedCraft(super.name, DateTime super.launchDate);
}

//https://dart.cn/language/#interfaces-and-abstract-classes
class MockSpaceship implements Spacecraft {
  String name;
  DateTime? launchDate;
  int? get launchYear => launchDate?.year;
  MockSpaceship(this.name, this.launchDate) {
    //Initialization code goes here.
  }
  //Named constructor that forwards to the default one.
  MockSpaceship.unlaunched(String name) : this(name, null);
  //method
  void describe() {
    print('MockSpaceship: $name');
    var launchDate = this.launchDate;
    if (launchDate != null) {
      int year = DateTime.now().difference(launchDate).inDays ~/ 365;
      print('Launched: $launchYear($year years ago)');
    } else {
      print('Unlaunched');
    }
  }
}

abstract class Describable {
  void describe();
  void describeWithEmphasis() {
    print('========');
    describe();
    print('========');
  }
}

//https://dart.cn/language/#async
const oneSecond = Duration(seconds: 1);
Future<void> printWithDelay(String message) async {
  await Future.delayed(oneSecond);
  print(message);
}

//上面的方法相当于：
/*Future<void> printWithDelay(String message) {
*  return Future.delayed(oneSecond).then((_) {
*    print(message);
* });
*}
*/

Future<void> createDescriptions(Iterable<String> objects) async {
  for (final object in objects) {
    try {
      var file = File('$object.txt');
      if (await file.exists()) {
        var modified = await file.lastModified();
        print('File for $object already exists. It was modified on $modified');
        continue;
      }
      await file.create();
      await file.writeAsString('Start describing $object in this file.');
    } on IOException catch (e) {
      print('Cannot create description for $object: $e');
    }
  }
}

Stream<String> report(Spacecraft craft, Iterable<String> objects) async* {
  for (final object in objects) {
    await Future.delayed(oneSecond);
    yield '${craft.name} files by $object';
  }
}

//https://dart.cn/language/#exceptions
Future<void> describeFlybyObjects(List<String> flybyObjects) async {
  try {
    for (final object in flybyObjects) {
      var description = await File('$object.txt').readAsString();
      print(description);
    }
  } on IOException catch (e) {
    print('Could not describe object: $e');
  } finally {
    flybyObjects.clear();
  }
}
